﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Navigation;
using Microsoft.Phone.Controls;
using System.Diagnostics;

namespace Odyssey.Controls
{
    /// <summary>
    /// Manages transitions between pages.
    /// </summary>
    public sealed class PageTransitionService : DependencyObject
    {
        private static NavigationMode currentNavigationMode;
        private static Uri currentUri;
        private static Uri parentUri;

        /// <summary>
        /// Gets or sets the image of the current page which can be used for the page to navigate to to perform a transition with it.
        /// </summary>
        public static Image PageImage { get; set; }


        /// <summary>
        /// Gets the currently loaded page.
        /// </summary>
        public static Page CurrentPage { get; private set; }

        /// <summary>
        /// Gets the current PageTransition that animates the page, otherwise null.
        /// </summary>
        public static PageAnimation CurrentTransition { get; private set; }


        /// <summary>
        /// Gets whether the associated Page is currently animating to another page.
        /// </summary>
        public static bool IsAnimating { get; private set; }


        /// <summary>
        /// Gets or sets whether the Back button will cause to raise NavigationService.GoBack().
        /// This is an attached dependency property.
        /// </summary>
        public static bool GetIsBackEnabled(DependencyObject obj)
        {
            return (bool)obj.GetValue(IsBackEnabledProperty);
        }

        public static void SetIsBackEnabled(DependencyObject obj, bool value)
        {
            obj.SetValue(IsBackEnabledProperty, value);
        }

        // Using a DependencyProperty as the backing store for IsBackEnabled.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty IsBackEnabledProperty =
            DependencyProperty.RegisterAttached("IsBackEnabled", typeof(bool), typeof(PageTransitionService), new PropertyMetadata(true));



        public static PageAnimationGroup GetPageTransitionGroup(DependencyObject obj)
        {
            return (PageAnimationGroup)obj.GetValue(PageTransitionGroupProperty);
        }

        public static void SetPageTransitionGroup(DependencyObject obj, PageAnimationGroup value)
        {
            obj.SetValue(PageTransitionGroupProperty, value);
        }

        public static readonly DependencyProperty PageTransitionGroupProperty =
            DependencyProperty.RegisterAttached("PageTransitionGroup", typeof(PageAnimationGroup), typeof(PageTransitionService), new PropertyMetadata(null, OnPageTransitionGroupPropertyChanged));


        private static void OnPageTransitionGroupPropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
        {
            PhoneApplicationPage page = o as PhoneApplicationPage;
            if (page == null) throw new InvalidCastException("Page expected.");
            PageAnimationGroup newGroup = e.NewValue as PageAnimationGroup;
            if (e.OldValue != null)
            {
                PhoneApplicationPage oldPage = e.OldValue as PhoneApplicationPage;
                if (oldPage != null)
                {
                    oldPage.Loaded -= OnPageLoaded;
                    oldPage.Unloaded -= OnPageUnloaded;
                    oldPage.BackKeyPress -= OnBackKeyPress;
                    UnloadPage(oldPage);
                }
            }
            page.Loaded += new RoutedEventHandler(OnPageLoaded);
            page.Unloaded += new RoutedEventHandler(OnPageUnloaded);
            if (!(page is AnimationPhoneApplicationPage))
            {
                // TransitionPhoneApplicationPage is checking BackKeyPress in a better order that allows hocked events to cancel correctly, so
                // this event handler is not necassary for that class.
                page.BackKeyPress += new EventHandler<System.ComponentModel.CancelEventArgs>(OnBackKeyPress);
            }
        }

        static void OnPageUnloaded(object sender, RoutedEventArgs e)
        {
            Page page = sender as Page;
            if (CurrentPage == page) CurrentPage = null;
            UnloadPage(page);
        }

        private static void OnPageLoaded(object sender, RoutedEventArgs e)
        {
            Page page = sender as Page;
            CurrentPage = page;
            LoadPage(page);
        }

        private static void UnloadPage(Page page)
        {
            page.NavigationService.Navigating -= OnNavigating;
            page.NavigationService.Navigated -= OnNavigated;
        }

        private static void LoadPage(Page page)
        {
            if (page == null) return;
            PageAnimationGroup group = GetPageTransitionGroup(page);
            if (group != null)
            {
                NavigationService service = page.NavigationService;
                if (service != null)
                {
                    service.Navigating += new NavigatingCancelEventHandler(OnNavigating);
                    service.Navigated += new NavigatedEventHandler(OnNavigated);
                }
            }
        }

        private static void OnTransitionCompleted(object sender, EventArgs e)
        {
            CurrentTransition.Completed -= OnTransitionCompleted;
            PageAnimation pageTransition = sender as PageAnimation;
            Page page = CurrentPage;
            if (CurrentTransition != null && page != null)
            {
                switch (currentNavigationMode)
                {
                    case NavigationMode.New:
                    case NavigationMode.Forward:
                        if (currentUri != null)
                        {
                            IsAnimating = true;
                            page.NavigationService.Navigate(currentUri);
                        }
                        break;

                    case NavigationMode.Back:
                        if (page.NavigationService.CanGoBack)
                        {
                            IsAnimating = true;
                            page.NavigationService.GoBack();
                        }
                        break;
                }
            }
        }

        public static void OnBackKeyPress(object sender, System.ComponentModel.CancelEventArgs e)
        {
            if (IsAnimating)
            {
                e.Cancel = true;
            }
            else
            {
                IsAnimating = false;
                Page page = sender as Page;
                if (page != null)
                {
                    if (GetIsBackEnabled(page))
                    {
                        NavigationService service = page.NavigationService;
                        if (service.CanGoBack)
                        {
                            PageAnimationGroup group = PageTransitionService.GetPageTransitionGroup(page);
                            if (group != null)
                            {
                                PageAnimation transition = group.GetTransition(PageAnimationMode.Hide, parentUri);
                                CurrentTransition = transition;
                                if (transition != null)
                                {
                                    if (transition.Hide(page))
                                    {
                                        transition.Completed += new EventHandler(OnTransitionCompleted);
                                        e.Cancel = true;
                                        currentNavigationMode = NavigationMode.Back;
                                        IsAnimating = true;
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }

        private static void OnNavigated(object sender, NavigationEventArgs e)
        {
            if (CurrentTransition != null && CurrentPage != null)
            {
                CurrentTransition.OnNavigated(CurrentPage);
                Page page = e.Content as Page;
                CurrentTransition = null;
                if (page != null)
                {
                    NavigateFrom(page);
                }
            }
        }

        private static void OnNavigating(object sender, NavigatingCancelEventArgs e)
        {
            if (e.Uri.IsAbsoluteUri) return;
            NavigationService service = sender as NavigationService;
            parentUri = service.Source;
            if (!IsAnimating)
            {
                PageAnimationMode mode = GetMode(e.NavigationMode);

                switch (mode)
                {
                    case PageAnimationMode.Show:
                        NavigateTo(e, service);
                        break;
                }
            }
            else
            {
                currentUri = null;
                IsAnimating = false;
            }
        }

        private static void NavigateFrom(Page page)
        {
            NavigationService service = page.NavigationService;
            PageAnimationMode mode = PageAnimationMode.Show;
            PageAnimationGroup group = PageTransitionService.GetPageTransitionGroup(page);
            currentNavigationMode = NavigationMode.Refresh;
            if (group != null)
            {
                currentUri = parentUri;
                PageAnimation transition = group.GetTransition(mode, currentUri);
                CurrentTransition = transition;
                if (transition != null)
                {
                    if (transition.Show(page))
                    {
                        IsAnimating = true;
                        transition.Completed += (s, e) => IsAnimating = false;
                    }
                }
            }
        }

        private static void NavigateTo(NavigatingCancelEventArgs e, NavigationService service)
        {
            PageAnimationMode mode = PageAnimationMode.Show;
            PageAnimationGroup group = PageTransitionService.GetPageTransitionGroup(CurrentPage);
            currentNavigationMode = e.NavigationMode;
            PageAnimation transition = group.GetTransition(mode, e.Uri);
            CurrentTransition = transition;
            currentUri = e.Uri;
            if (transition != null)
            {
                bool animate = transition.Hide(CurrentPage);
                if (animate)
                {
                    transition.Completed += new EventHandler(OnTransitionCompleted);
                    IsAnimating = true;
                    e.Cancel = true;
                }
            }
        }

        private static PageAnimationMode GetMode(NavigationMode navigationMode)
        {
            switch (navigationMode)
            {
                case NavigationMode.Forward:
                case NavigationMode.New:
                    return PageAnimationMode.Show;

                case NavigationMode.Back:
                    return PageAnimationMode.Hide;

                default: return PageAnimationMode.None;
            }
        }

    }
}
